package pacman.entries.pacman.meta_graph;

import java.util.Collection;
import java.util.LinkedList;

import pacman.game.Game;
import pacman.game.Constants.DM;
import pacman.game.Constants.MOVE;

public class MetaGraphPath implements IMetaGraphObject {

	public MOVE nextMove(Game game) {
		int index = game.getPacmanCurrentNodeIndex();
		
		MetaGraphNode destNode = m_Node;
		if (index == m_Node.m_Index) {
			if (m_Edge == null)
				return MOVE.NEUTRAL;
			destNode = m_Edge.getOppositeNodeOf(m_Node);
		}
		
		return game.getNextMoveTowardsTarget(index, destNode.m_Index, DM.PATH);
	}
	
	// _______________________ UTILITIES
	
	@Override
	public String toString() {
		
		String str = "(" + m_Node.getIndex();
		if (m_Edge == null)
			str = ")";
		else {
			str += "," + m_Edge.getOppositeNodeOf(m_Node).m_Index + ")";
		}
		
		if (m_Next != null)
			str += ",\t" + m_Next.toString();
		
		return str;
	}
	
	public Collection<MetaGraphPath> PathsOfNextLength() {
		LinkedList<MetaGraphPath> list = new LinkedList<MetaGraphPath>();
		
		MetaGraphPath lastPath = Last();
		MetaGraphNode lastNode = null;
		if (lastPath.m_Edge == null) {
			lastNode = lastPath.m_Node;
			for (MetaGraphEdge edge : lastNode.getAdjacentMetaEdges()) {
				MetaGraphPath newPath = lastPath.Copy();
				newPath.m_Edge = edge;
				list.add(newPath);
			}
		}
		else {
			lastNode = lastPath.m_Edge.getOppositeNodeOf(lastPath.m_Node);
			for (MetaGraphEdge edge : lastNode.getAdjacentMetaEdges()) {
				MetaGraphPath copy = lastPath.Copy();
				MetaGraphPath newPath = new MetaGraphPath(lastNode, edge, copy, null);
				copy.Last().m_Next = newPath;
				list.add(copy);
			}
		}
		return list;
	}
	
	public int Size() {
		if (m_Next == null) return 1;
		return m_Next.Size() + 1;
	}
	
	public float Length(Game game) {
		
		float length = 0.0f;
		
		if (m_Prev == null)
			length += game.getDistance(game.getPacmanCurrentNodeIndex(), m_Node.getIndex(), DM.PATH);
			
		if (m_Next != null)
			length += m_Next.Length(game);
		
		if (m_Edge != null)
			length += game.getDistance(m_Edge.getNode1().getIndex(), m_Edge.getNode2().getIndex(), DM.PATH);
		
		return length;
	}
	
	public MetaGraphPath Last() {
		if (m_Next == null)
			return this;
		return m_Next.Last();
	}
	
	public MetaGraphPath Copy() {
		
		if (m_Prev != null)
			return m_Prev.Copy();
		
		return ForwardCopy();
	}
	
	private MetaGraphPath ForwardCopy() {

		MetaGraphPath nextCopy = null;
		if (m_Next != null)
			nextCopy = m_Next.ForwardCopy();
		
		MetaGraphPath copy = new MetaGraphPath(m_Node, m_Edge, null, nextCopy);
		
		if (nextCopy != null)
			nextCopy.m_Prev = copy;
		
		return copy;
	}
	
	// _______________________ CONSTRUCTORS
	
	public MetaGraphPath(MetaGraphNode node) {
		setProperties(node, null, null, null);
	}
	
	public MetaGraphPath(MetaGraphNode node, MetaGraphEdge edge) {
		setProperties(node, edge, null, null);
	}
	
	public MetaGraphPath(MetaGraphNode node, MetaGraphEdge edge, MetaGraphPath prev) {
		setProperties(node, edge, prev, null);
	}
	
	public MetaGraphPath(MetaGraphNode node, MetaGraphEdge edge, MetaGraphPath prev, MetaGraphPath next) {
		setProperties(node, edge, prev, next);
	}
	
	private void setProperties(MetaGraphNode node, MetaGraphEdge edge, MetaGraphPath prev, MetaGraphPath next) {
		m_Node = node;
		m_Edge = edge;
		m_Prev = prev;
		m_Next = next;
	}
	
	// _______________________ STRUCTURE
	
	// A path has at least a starting node
	MetaGraphNode m_Node;
	public MetaGraphNode Node() { return m_Node; }
	
	// A path may have an edge
	MetaGraphEdge m_Edge;
	public MetaGraphEdge Edge() { return m_Edge; }
	
	// A path may have a preceding path
	MetaGraphPath m_Prev;
	public MetaGraphPath Prev() { return m_Prev; }
	
	// A path may have a next path
	MetaGraphPath m_Next;
	public MetaGraphPath Next() { return m_Next; }
	
	// _______________________ SCORE
	
	@Override
	public float calcScore(Game game, MetaGraphEvaluator evaluator) {
		
		float score = 0.0f;
		
		// if the first part of the path
		if (m_Prev == null) {
			score += evaluator.Evaluate(game, m_Node);
			score += evaluator.Evaluate(game, this);
		}
		
		score += 0.9f * evaluator.Evaluate(game, m_Edge);
		
		if (m_Next != null)
			score += m_Next.calcScore(game, evaluator);
		
		return score;
	}
	
	// ______________ OLD
	
	/*List<MetaGraphEdge> m_Edges;
	
	public MetaGraphPath(MetaGraphNode startNode, List<MetaGraphEdge> edges) {
		if (startNode == null)
			throw new IllegalArgumentException("Argument startNode cannot be null.");
		if (edges == null)
			throw new IllegalArgumentException("Argument edges cannot be null.");
		
		m_StartNode = startNode;
		m_Edges = edges;
	}
	
	@Override
	public float calcScore(Game game, MetaGraphEvaluator evaluator) {
		
		float score = evaluator.Evaluate(game, this);
		
		score += m_StartNode.calcScore(game, evaluator);
		
		//for (MetaGraphEdge edge : m_Edges)
		//	score += edge.calcScore(game, evaluator);
		
		return score;
	}*/

}
